﻿/**********************************************************
 * Control2Png.cs
 * Renders a Silverlight UIElement as an uncompressed PNG 
 * stream or byte array. Relies on Joe Stegman's great
 * PNGEncoder classes.
 *
 * Written by : Pierre BELIN <pierre@ree7.fr>
 *
 * Distributed under the Microsoft Public License (Ms-PL).
 * See accompanying file License.txt
 *
 **********************************************************/

using Ree7.IO.Png;
using System;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Ree7.Utils.Imaging
{
	/// <summary>
	/// See http://ree7.fr/blog/2010/10/composition-dimage-rendre-du-xaml-sous-forme-dimage/
	/// </summary>
	public class ControlToPng
	{
		public static void RenderAsPNGStream(UIElement e, Stream s)
		{
			e.UpdateLayout();

			WriteableBitmap wb = new WriteableBitmap((int)e.DesiredSize.Width, (int)e.DesiredSize.Height);
			wb.Render(e, null);
			wb.Invalidate();

            int[] pixels = wb.Pixels;
            int pixelHeight = wb.PixelHeight;
            int pixelWidth = wb.PixelWidth;
            wb = null;

            // Convert pixel format
            for (int i = 0; i < pixels.Length; i++)
            {
                pixels[i] = ExtractRGBAfromPremultipliedARGB(pixels[i]);
            }

			PngEncoder2 encoder = new PngEncoder2();
			encoder.Encode(ref pixels, pixelWidth, pixelHeight, s);
		}

        [Obsolete]
        public static byte[] RenderAsPNGBytes(UIElement e)
        {
            MemoryStream s = new MemoryStream();
            RenderAsPNGStream(e, s);

            return s.GetBuffer();
        }

		/// <summary>
		/// Convert from premultiplied alpha ARGB to a non-premultiplied RGBA (fix 12/2011)
		/// </summary>
		/// <seealso cref="http://nokola.com/blog/post/2010/01/27/The-Most-Important-Silverlight-WriteableBitmap-Gotcha-Does-It-LoseChange-Colors.aspx"/>
		public static int ExtractRGBAfromPremultipliedARGB(int pARGB)
		{
			byte[] sourcebytes = new byte[4];
			sourcebytes[0] = (byte)(pARGB >> 24);
			sourcebytes[1] = (byte)((pARGB & 0x00FF0000) >> 16);
			sourcebytes[2] = (byte)((pARGB & 0x0000FF00) >> 8);
			sourcebytes[3] = (byte)(pARGB & 0x000000FF);

			if (pARGB == 0) return BitConverter.ToInt32(sourcebytes, 0); // optimization for images with many transparent pixels
			
			byte[] destbytes = new byte[4];

			if (sourcebytes[0] == 0 || sourcebytes[0] == 255)
			{
				destbytes[0] = sourcebytes[1];
				destbytes[1] = sourcebytes[2];
				destbytes[2] = sourcebytes[3];
				destbytes[3] = sourcebytes[0];
			}
			else
			{
				double factor = 255.0 / sourcebytes[0];
				double r = sourcebytes[1] * factor;
				double g = sourcebytes[2] * factor;
				double b = sourcebytes[3] * factor;

				destbytes[0] = Convert.ToByte(Math.Min(Byte.MaxValue, r));
				destbytes[1] = Convert.ToByte(Math.Min(Byte.MaxValue, g));
				destbytes[2] = Convert.ToByte(Math.Min(Byte.MaxValue, b));
				destbytes[3] = sourcebytes[0];
			}

            return BitConverter.ToInt32(destbytes, 0); ;
		}
	}
}
